package com.itrip.log.module.handler;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.itrip.log.core.ReaderBean;
import com.itrip.log.module.db.KVDB;

/**
 * Function:process dubborpc.log
 *
 * 
 * @date:2016年10月13日/下午12:05:30
 * @Author:jeff@aoliday.cao
 * @version:1.0
 */
public class DubboLogHandlerv2 extends AbstractHandler {

	protected static final String DATE_REGX = "[\\d]{4}-[\\d]{2}-[\\d]{2} [\\d]{2}:[\\d]{2}:[\\d]{2}";
	protected static final Pattern RPC_LOG_PAT = Pattern.compile("((API|method|spendTime)\\[(.*?)\\])");
	protected static final Pattern DB_LOG_PAT = Pattern.compile("(ItripBaseDao)|(\\s\\w{1,}\\.\\w{1,})|(time=\\d)");
	protected static final Pattern URL_LOG_PAT = Pattern.compile("http[s]?://[\\w\\.]*([/\\w]*)[\\?\\w=\\w]*\\s(\\d*)");
	private Logger log = LoggerFactory.getLogger(DubboLogHandlerv2.class);
	private HashMap<String, int[]> count = new HashMap<String, int[]>();
	private static final String SPLITER = "!";
	private KVDB kvdb;

	public void setKvdb(KVDB kvdb) {
		this.kvdb = kvdb;
	}

	/**
	 * line:2016-10-13 11:00:35 [INFO] RPC info: API[ XXX ],method[XX ],
	 * args[[\"X\"] ], spendTime[ 0 ms ]
	 */
	@Override
	public synchronized void handle(ReaderBean bean) {
		StringBuilder sb = new StringBuilder();
		String file = bean.getFile();
		int spendTime = 0;
		if (file.endsWith("dubborpc.log")) {
			kvdb.hincr("proc_pv", bean.getProc());
			spendTime = getRPCSpendTime(bean, sb);
		} else if (file.endsWith("db.log")) {
			spendTime = getDbSpendTime(bean, sb);
		} else if (file.endsWith("request.log")) {
			kvdb.hincr("proc_pv", bean.getProc());
			spendTime = getreReqSpendTime(bean, sb);
		}
		if (sb.length() == 0) {
			log.debug("error foramt rpc log {}", bean);
			return;
		}
		String key = sb.toString();
		int[] info = count.get(key);
		if (info == null) {
			info = new int[9];
			info[0] = 1;// times
			info[1] = spendTime;// max
			info[2] = spendTime;// min
			info[3] = spendTime;// total
			rangeCount(spendTime, info);
			count.put(key, info);
		} else {
			info[0] += 1;// times
			info[3] += spendTime;// total
			info[1] = Math.max(info[1], spendTime);// max
			info[2] = Math.min(info[2], spendTime);// min
			rangeCount(spendTime, info);
		}
	}

	// INFO 2016-02-24 14:44:56 [TimingLogFilter.java:65] 192.168.0.25 55150 <--http://test33.itrip.com/main/getCurrency?date=1456296189007 1
	private int getreReqSpendTime(ReaderBean bean, StringBuilder sb) {
		int speedTime = 0;
		Matcher matcher = URL_LOG_PAT.matcher(bean.getLine());
		if (matcher.find()) {// api
			// url接口参数为null
			String api = "api";
			String path = matcher.group(1);
			String method = path.length() < 128 ? path : path.substring(0, 127);
			sb.append(buildKey(bean, api, method, "url"));
			String group = matcher.group(matcher.groupCount()).trim();
			if (group.length() > 0)
				speedTime = Integer.valueOf(group);
		}
		return speedTime;
	}

	private int getDbSpendTime(ReaderBean bean, StringBuilder sb) {
		Matcher matcher = DB_LOG_PAT.matcher(bean.getLine());
		int spendTime = 0;
		if (matcher.find()) {// api
			String api = matcher.group();
			if (matcher.find()) {// sqlId
				String sqlId = matcher.group();
				sb.append(buildKey(bean, api, sqlId, "db"));
			}
			if (matcher.find()) {// times
				String times = matcher.group().trim();
				int index = times.lastIndexOf("=");
				if (index > -1) {
					spendTime = Integer.parseInt(times.substring(index + 1));
				}
			}
		}
		return spendTime;
	}

	private int getRPCSpendTime(ReaderBean bean, StringBuilder sb) {
		Matcher matcher = RPC_LOG_PAT.matcher(bean.getLine());
		int spendTime = 0;
		if (matcher.find()) {// api
			int mcount = matcher.groupCount();
			String api = matcher.group(mcount).trim();
			if (matcher.find()) {// method
				String method = matcher.group(mcount).trim();
				sb.append(buildKey(bean, api, method, "dubbo"));
			}
			if (matcher.find()) {// spendTime
				String trim = matcher.group(mcount).trim();
				int index = trim.indexOf(" ms");
				if (index > -1)
					spendTime = Integer.valueOf(trim.substring(0, index));
			}
		}
		return spendTime;
	}

	private String buildKey(ReaderBean bean, String api, String method, String staticType) {
		StringBuilder sb = new StringBuilder();
		sb.append(bean.getHost()).append(SPLITER);
		sb.append(bean.getProc()).append(SPLITER);
		sb.append(api).append(SPLITER);
		sb.append(method).append(SPLITER);
		sb.append(staticType);
		return sb.toString();
	}

	private void rangeCount(int spendTime, int[] info) {
		if (spendTime <= 100) {
			info[4] += 1;
		} else if (spendTime <= 500) {
			info[5] += 1;
		} else if (spendTime <= 1000) {
			info[6] += 1;
		} else if (spendTime <= 5000) {
			info[7] += 1;
		} else {
			info[8] += 1;
		}
	}

	@Override
	public boolean match(String file) {
		return file.endsWith("dubborpc.log") || file.endsWith("db.log") || file.endsWith("request.log");
	}

	/***
	 * 周期性将汇总结果转成SQL put到异步队列入库,周期在spring-task配置
	 * 
	 * @return
	 */
	public synchronized void conrvetSumResult2SQL() {
		checkIsNecessaryCreateNewTable();
		if (count.isEmpty()) {
			log.debug("sum result is empty");
			return;
		}
		long now = System.currentTimeMillis() / 60000;
		StringBuilder sqls = new StringBuilder();
		for (Entry<String, int[]> item : count.entrySet()) {
			String[] key = item.getKey().split(SPLITER);
			try {
				int[] value = item.getValue();
				String server = key[0];
				String proc = key[1];
				String api = key[2];
				String method = key[3];
				String staticType = key[4];
				int calls = value[0];
				int maxTime = value[1];
				int minTime = value[2];
				int totalTimes = value[3];
				int t100ms = value[4];
				int t500ms = value[5];
				int t1s = value[6];
				int t5s = value[7];
				int gt5s = value[8];

				// 保留2位小数
				double avg = (totalTimes * 100 / calls) / 100.0;
				String sql = String
						.format("INSERT INTO static_method_m1(static_type,server,proc,api,method,calls,min_time_cost,max_time_cost,avg_time_cost,t1,t2,t3,t4,t5,create_time) VALUES('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');",
								staticType, server, proc, api, method, calls, minTime, maxTime, avg, t100ms, t500ms,
								t1s, t5s, gt5s, now);
				sqls.append(sql);
			} catch (Exception e) {
				log.error("to sql error:" + item.getKey(), e);
			}
		}
		log.info("conrvet [{}] sum result to sql and put to aysn queue", count.size());
		ansySQLExecutor.putSQL(sqls.toString());
		count.clear();
	}

	/**
	 * 每天00:00创建新表
	 */
	private void checkIsNecessaryCreateNewTable() {
		Calendar now = Calendar.getInstance();
		if (now.get(Calendar.HOUR_OF_DAY) == 0 && now.get(Calendar.MINUTE) == 0) {
			ansySQLExecutor.putSQL(doParation("static_method_m1", now));
		}
	}

	/***
	 * <pre>
	 * alter table xxx rename xxx_year_month_date
	 * create table if not exists xxx like xxx_year_month_date
	 * INSERT INTO static_url_m1 (...) values(...)
	 * </pre>
	 * 
	 * @param table
	 * @param now
	 * @return
	 */
	private String doParation(String table, Calendar now) {
		int year = now.get(Calendar.YEAR);
		int date = now.get(Calendar.DATE);
		int month = now.get(Calendar.MONTH) + 1;
		String newName = String.format("%s_%s_%02d_%02d", table, year, month, date - 1);
		String rename = String.format("alter table %s rename %s", table, newName);
		String create = String.format("create table if not exists %s like %s", table, newName);
		return String.format("%s;%s;", rename, create);
	}
}
